package net.cellkyborg.RSInstances;

import java.net.Socket;
import java.net.SocketException;
import java.util.LinkedList;
import java.util.StringTokenizer;
import java.io.*;

import net.cellkyborg.server.*;

public class RSPlayer extends RSAttackable implements Runnable, PlayerConstants {

	private Socket mySock = null;
	public int playerID;

	public boolean savefile, loaded;

	private InputStream in;
	private OutputStream out;
	public static final int bufferSize = 1000000;
	public stream inStream = null, outStream = null;
	public Cryption inStreamDecryption = null, outStreamDecryption = null;
	public boolean hasHouseOptionsOpen;
	public int lowMemoryVersion, playerRights, playerIsMember, playerMessages,
			playerLastLogin, playerEnergy, playerSkull, playerIcons, playerDesigned;
	public int[] playerColor, playerXP, playerLevel, playerEquipment,
			playerEquipmentN, playerItems, playerItemsN, bankItems, bankItemsN;
	public long[] playerFriends, playerIgnores;
	public String playerName, playerPass, playerLastConnect;
	public int pHead, pBeard, pTorso, pArms, pHands, pLegs, pFeet, ancients,
			totalQP, specialEnergy;
	public int[] emotes;
	public int returnCode = 40;
	public int pingNumber = 0, pingDelay = 0;
	public boolean active = true;
	public long lastTimePacketReceived;

	private boolean appearanceUpdateRequired;
	private boolean inventoryUpdateRequired;

	int[] packetSizes = { 
			-3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
			-3, -1, -3, -3, -3, -3, -3, -3, -3, -3,
			-3, -3, -3, -3, -3, -3, -3, -3, -3, -3, 
			-3, -3, -3, -3, 1, -3, -3, -3, -3, -3, 
			-3, -3, -3, -3, -3, -3, -1, -3, -3, -3,
			-3, -3, -1, -3, -3, -3, -3, -3, -3, -1,
			-3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
			-3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
			-3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
			-3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
			-3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
			-3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
			-3, -1, -3, -3, 8, -3, -3, -3, -3, -3,
			-3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
			-3, -3, -3, -3, -3, -3, -3, -3, -3, -1,
			-3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
			-3, -3, -3, -3, -3, -3, -3, 4, -3, -3,
			-3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
			-3, -3, -3, -3, -3, -3, 0, -3, -3, -3,
			-3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
			-3, -3, -1, -3, -3, -3, -3, -3, -3, -3,
			-3, -3, -3, -3, -3, 4, -3, -3, -3, -3,
			-3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
			-3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
			-3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
			-3, -3, -3, -3, -3, -3
	};

	private ButtonHandler buttonHandler;
	private LinkedList<RSPlayer> playerList = new LinkedList();
	private LinkedList<RSNPC> npcList = new LinkedList();

	public RSPlayer(Socket s, int id) {
		super();
		playerID = id;
		mySock = s;

		savefile = false;
		loaded = false;

		playerColor = new int[6];
		playerXP = new int[25];
		playerLevel = new int[25];

		playerEquipment = new int[14];
		playerEquipmentN = new int[14];
		playerItems = new int[28];
		playerItemsN = new int[28];

		bankItems = new int[800];
		bankItemsN = new int[800];
		playerFriends = new long[100];
		playerIgnores = new long[100];
		
		hasHouseOptionsOpen = false;

		for (int i = 0; i < playerEquipment.length; i++) {
			playerEquipment[i] = -1;
			playerEquipmentN[i] = -1;
		}
		
		for (int i = 0; i < playerItems.length; i++) {
			playerItems[i] = -1;
			playerItemsN[i] = -1;
		}
		
		emotes = new int[4];
		playerSkull = -1;
		playerIcons = -1;
		updateRequired = true;
		animationUpdateRequired = true;
		appearanceUpdateRequired = true;
		inventoryUpdateRequired = false;
		(new Thread(this)).start();
	}

	public void run() {
		try {
			in = mySock.getInputStream();
			out = mySock.getOutputStream();

			outStream = new stream(new byte[bufferSize]);
			outStream.writeOffset = 0;
			outStream.readOffset = 0;
			inStream = new stream(new byte[bufferSize]);
			inStream.writeOffset = 0;
			inStream.readOffset = 0;

			long serverSessionKey = 0, clientSessionKey = 0;
			serverSessionKey = ((long) (java.lang.Math.random() * 99999999D) << 32)
					+ (long) (java.lang.Math.random() * 99999999D);

			fillInStream(1);
			int namePart = inStream.readUnsignedByte();

			outStream.writeByte(0);
			outStream.writeQWord(serverSessionKey);
			directFlushOutStream();

			fillInStream(2);
			int loginType = inStream.readUnsignedByte();
			if (loginType != 16 && loginType != 18) {
				misc.println("Unexpected login type " + loginType);
				return;
			}
			int loginPacketSize = inStream.readUnsignedByte();
			System.out.println("Login packet size: " + loginPacketSize);
			if (loginPacketSize - 93 <= 0) {
				misc.println("Zero RSA packet size!");
				return;
			}
			fillInStream(loginPacketSize);

			if (inStream.readDWord() != 474) {
				misc.println("Not supported version");
				return;
			}

			lowMemoryVersion = inStream.readUnsignedByte();

			for (int i = 0; i < 24; i++)
				System.out.print(inStream.readUnsignedByte() + " ");

			for (int i = 0; i < 16; i++) {
				String junk = Integer.toHexString(inStream.readDWord());
			}

			int tmp = inStream.readUnsignedByte();
			if (tmp != 10) {
				misc.println("Encrypted packet Id was " + tmp
						+ " but expected 10");
				return;
			}

			clientSessionKey = inStream.readQWord();
			serverSessionKey = inStream.readQWord();
			
			playerName = misc.longToPlayerName(inStream.readQWord());
			playerName.toLowerCase();

			if (playerName == null || playerName.length() == 0)
				return;

			playerPass = inStream.readString();
			if (playerPass.length() != loginPacketSize - 26 - 93)
				System.out.println("Login packet mismatch: "
						+ playerPass.length() + " "
						+ (loginPacketSize - 26 - 93));

			misc.println(playerName + " is signing onto server. " + playerPass);

			int sessionKey[] = new int[4];
			sessionKey[0] = (int) (clientSessionKey >> 32);
			sessionKey[1] = (int) clientSessionKey;
			sessionKey[2] = (int) (serverSessionKey >> 32);
			sessionKey[3] = (int) serverSessionKey;

			inStreamDecryption = new Cryption(sessionKey);
			for (int i = 0; i < 4; i++)
				sessionKey[i] += 50;

			outStreamDecryption = new Cryption(sessionKey);
			outStream.packetEncryption = outStreamDecryption;

			returnCode = 2;

			server.sqlHandler.readCharacter(this);
			
			server.sqlHandler.loadMoreInfo(this);

			teleportToX = teleportToY = -1;
			location[0] = 3086;
			location[1] = 3126;
			mapRegion[0] = (location[0] >> 3);
			mapRegion[1] = (location[1] >> 3);
			currentLocation[0] = location[0] - 8 * (mapRegion[0] - 6);
			currentLocation[1] = location[1] - 8 * (mapRegion[1] - 6);
			outStream.writeOffset = 0;
			outStream.writeByte(returnCode);
			if (returnCode != 2) {
				directFlushOutStream();
				return;
			}
			outStream.writeByte(0);
			outStream.writeByte(1);
			outStream.writeByte(0);
			outStream.writeByte(47);
			outStream.writeByte(0);
			outStream.createFrameVarSizeWord(ServerPackets.SET_MAP_REGION);
			outStream.writeWordBigEndianA(currentLocation[1]);
			outStream.writeByte(location[2]);
			outStream.writeWord(mapRegion[0]);

			boolean bool_33_ = false;

			if ((((mapRegion[0] / 8) == 48) || ((mapRegion[0] / 8) == 49))
					&& ((mapRegion[1] / 8) == 48)) {
				bool_33_ = true;
			}

			if (((mapRegion[0] / 8) == 48) && ((mapRegion[1] / 8) == 148)) {
				bool_33_ = true;
			}

			int i_28_ = 0;

			for (int i_34_ = (mapRegion[0] - 6) / 8; i_34_ <= ((mapRegion[0] + 6) / 8); i_34_++) {
				for (int i_35_ = (mapRegion[1] - 6) / 8; i_35_ <= ((mapRegion[1] + 6) / 8); i_35_++) {
					int i_36_ = i_35_ + (i_34_ * 256);

					if (!bool_33_
							|| ((i_35_ != 49) && (i_35_ != 149)
									&& (i_35_ != 147) && (i_34_ != 50) && ((i_34_ != 49) || (i_35_ != 47)))) {

						int[] mapData = WorldData.getMapAreaData(i_36_);

						outStream.writeDWordBigEndian(mapData[0]);
						outStream.writeDWordBigEndian(mapData[1]);
						outStream.writeDWordBigEndian(mapData[2]);
						outStream.writeDWordBigEndian(mapData[3]);
						i_28_++;
					}
				}
			}
			outStream.writeWordA(currentLocation[0]);
			outStream.writeWord(mapRegion[1]);
			outStream.endFrameVarSizeWord();

			outStream.createFrame(ServerPackets.LOGIN_FRAME15);
			outStream.writeByte(0);
			outStream.writeByte(0);
			outStream.writeByte(0);

			outStream.createFrame(ServerPackets.LOGIN_FRAME236);
			outStream.writeByte(78);
			outStream.writeByte(60);
			outStream.writeByte(48);
			outStream.writeByte(57);

			outStream.createFrame(ServerPackets.LOGIN_FRAME251);// main frame
																// interface
			outStream.writeWordBigEndian(548);// 548

			outStream.createFrame(ServerPackets.SET_INTERFACE);// cleans buggy
																// interface
			outStream.writeByteS(1);
			outStream.writeWordBigEndian(137);
			outStream.writeWord(90);
			outStream.writeWord(548);

			setInterface(1, 137, 90);

			frame156();

			setInterface(1, 320, 100);
			setInterface(1, 274, 101);
			setInterface(1, 149, 102);
			setInterface(1, 387, 103);
			setInterface(1, 271, 104);
			setInterface(1, 192, 105);
			setInterface(1, 589, 106);
			setInterface(1, 550, 107);

			sendStringFrame("Friend List", 38, 2, 2, 0);

			setInterface(1, 92, 99);
			setInterface(1, 551, 108);
			setInterface(1, 182, 109);
			setInterface(1, 261, 110);
			setInterface(1, 464, 111);
			setInterface(1, 239, 112);

			sendStringFrame("AUTO", 239, 0, 176, 0);

			sendSubMenuFrame("Follow", 128, 3);
			sendSubMenuFrame("Trade With", 128, 4);

			outStream.createFrame(ServerPackets.LOGIN_FRAME184);
			outStream.writeByteA(1);
			outStream.writeDWord(24313880);

			setInterface(1, 371, 77);

			outStream.createFrame(ServerPackets.SET_MUSIC);
			outStream.writeWordBigEndianA(62);// 190

			sendStringFrame("Newbie Melody", 239, 0, 176, 0);

			addPingPacket();

			frame100();

			setConfig(18, 1);

			highlightSprite(33554432, 22);

			setConfig(118, -1);
			setConfig(166, 2);
			setConfig(167, 0);
			setConfig(168, 4);
			setConfig(169, 0);
			setConfig(170, 0);
			setConfig(171, 0);
			setConfig(173, 0);
			setConfig(287, 0);

			highlightSprite(1000, 300);
			setConfig(304, 0);
			
			/*
			setConfig(406, 7);// tutorial island %
			*/
			highlightSprite(1073741824, 486);

			setConfig(520, 1);

			highlightSprite(-2147483648, 553);
			highlightSprite(1073741824, 623);
			highlightSprite(128, 788);
			highlightSprite(33554432, 810);

			setConfig(849, -1);
			setConfig(850, -1);
			setConfig(851, -1);
			setConfig(852, -1);
			setConfig(853, -1);
			setConfig(854, -1);
			setConfig(855, -1);
			setConfig(856, -1);
			setConfig(872, 0);

			highlightSprite(4194304, 913);

			setConfig(951, -1);
			setConfig(952, -1);
			setConfig(953, -1);
			setConfig(954, -1);
			setConfig(955, -1);
			setConfig(956, -1);
			setConfig(959, -1);
			setConfig(960, -1);
			setConfig(961, -1);
			setConfig(962, -1);
			setConfig(963, -1);

			highlightSprite(2048, 1010);

			setConfig(1040, 2);

			highlightSprite(262144, 1044);

			setConfig(1048, 32);

			highlightSprite(4096, 1050);

			setConfig(1065, -1);
			setConfig(1066, -1);
			setConfig(1067, -1);
			setConfig(1068, -1);
			setConfig(1069, -1);
			setConfig(1070, -1);

			highlightSprite(268435456, 1073);
			highlightSprite(512, 1077);
			highlightSprite(16560, 1083);

			updateThisPlayer(outStream);

			outStream.createFrame(ServerPackets.NPC_UPDATE);
			outStream.writeSize(1);
			outStream.writeByte(0);

			frame169(32, 32);
			frame169(40, 32);
			frame169(48, 32);
			frame169(56, 32);
			frame169(40, 40);
			frame169(48, 40);
			frame169(56, 40);
			frame169(64, 40);
			frame169(32, 48);
			frame169(40, 48);
			frame169(48, 48);

			outStream.createFrame(ServerPackets.LOGIN_FRAME188);// spawn object
			outStream.writeByte(42);// 42
			outStream.writeWordA(352);// 352
			outStream.writeByteS(83);// 45

			frame169(56, 48);
			frame169(64, 48);
			frame169(32, 56);
			frame169(40, 56);
			frame169(48, 56);
			frame169(56, 56);
			frame169(64, 56);
			frame169(32, 64);
			frame169(56, 64);
			frame169(64, 64);

			updateInventory();

			for (int i = 0; i < 25; i++) {
				setSkillLevelXP(i, playerLevel[i], playerXP[i]);
			}
			
			if (playerDesigned < 1) {
				setInterface(0, 269, 77);
				playerDesigned = 1;
			}
			
			//FRAME TESTING
			for(int i = 0; i < 100; i++){
			//	frame169(i, i);
			}
			//END OF FRAME TESTING
			
			frame66(10);

			setRunEnergy(playerEnergy);

			frame138();
			frame5(0);

			for (int i = 0; i < 6; i++) {
				outStream.createFrame(ServerPackets.LOGIN_FRAME4);
				outStream.writeByte(i);
				for (int j = 0; j < 19; j++)
					outStream.writeByte(0);
			}

			directFlushOutStream();

			buttonHandler = new ButtonHandler(this);

			lastTimePacketReceived = System.currentTimeMillis();
			savefile = true;
			// disconnected = false;
			loaded = true;
			System.out.println("Finished first packet");
		} catch (IOException ioex) {
			ioex.printStackTrace();
			savefile = false;
			// disconnected = true;
			destroy();
			loaded = true;
		}
	}
	
	public void preProcessing() {
		resetPlayerUpdateVars();

		try {
			pingDelay++;
			if (pingDelay == 10) {
				addPingPacket();
				pingDelay = 0;
			}
			int packetSize = in.available();
			// System.out.println(packetSize);
			if (packetSize > 0) {
				fillInStream(packetSize);
				// showBuffer(packetSize);
				while (parseData())
					;

				if (inStream.available() < 1) {
					inStream.writeOffset = 0;
					inStream.readOffset = 0;
				}
				lastTimePacketReceived = System.currentTimeMillis();
				// inStream.currentOffset+=packetSize-1;
			}

			if (System.currentTimeMillis() - lastTimePacketReceived > 60000) {
				destroy();
			}

		} catch (IOException ioex) {
			ioex.printStackTrace();
		}
	}

	private boolean parseData() {
		if (inStream.available() < 1)
			return false;

		int packetType = inStream.readUnsignedByte();
		int packetSize = packetSizes[packetType];

		if (packetSize >= 0) {
			if (inStream.available() < packetSize) {
				inStream.readOffset -= 1;
				return false;
			} else {
				return handleData(packetType, packetSize);
			}
		} else if (packetSize == -1) {
			if (inStream.available() < 1) {
				inStream.readOffset -= 1;
				return false;
			} else {
				packetSize = inStream.readUnsignedByte();
				if (inStream.available() < packetSize) {
					inStream.readOffset -= 2;
					return false;
				} else {
					return handleData(packetType, packetSize);
				}
			}
		} else if (packetSize == -2) {
			if (inStream.available() < 2) {
				inStream.readOffset -= 1;
				return false;
			} else {
				packetSize = inStream.readSize();
				if (inStream.available() < packetSize) {
					inStream.readOffset -= 3;
					return false;
				} else {
					return handleData(packetType, packetSize);
				}
			}
		} else {
			int avail = inStream.available();
			// printBuffer(avail);
			handleData(packetType, avail);
			return false;
		}
	}

	private boolean handleData(int packetType, int packetSize) {
		// misc.println("PacketType: "+packetType);

		switch (packetType) {

		case ClientPackets.WINDOW_STATE:// window state change (active, not
			// active)
			int actv = inStream.readUnsignedByte();
			if (actv == 0)
				active = false;
			else if (actv == 1)
				active = true;
			break;
		case ClientPackets.WIELD_ITEM:// wield item
			int slot = inStream.readUnsignedByteA();
			int junk2 = inStream.readUnsignedByte();// 0
			int itemID = inStream.readUnsignedWord();
			int junk = inStream.readDWordBigEndian();// 0 0 149 0

			equipItem(itemID, slot);
			misc.println("Wield item id: " + itemID + " slot: " + slot
					+ " Junk1: " + junk + " junk2: " + junk2);
			break;
		case ClientPackets.CHANGE_SLOT_INV:// change inventory item place
			inStream.readOffset += 6;
			int slot1 = inStream.readUnsignedByteA();
			int slot2 = inStream.readUnsignedByte();
			int junk1 = inStream.readUnsignedByte();
			misc.println("Change item place from " + slot1 + " to " + slot2
					+ " junk1: " + junk1);
			// showBuffer(packetSize);
			break;
		case ClientPackets.CAMERA_CHANGE: // camera angle change
			inStream.readOffset += packetSize;
			break;
			
		case ClientPackets.SPRITE_CLICK: //clicking sprites?
		case ClientPackets.SPRITE_BUTTON: //clicking sprite butons?
		case ClientPackets.BUTTON_PRESSED: // button pressed
			int interID = inStream.readSize();
			int buttID = inStream.readSize();
			misc.println("Button pressed: Iterface ID - " + interID
					+ " Button ID - " + buttID);
			buttonHandler.buttonPressed(interID, buttID);
			break;
			
		case ClientPackets.X_CLOSE_BUTTON:
			if(hasHouseOptionsOpen == true){
				setInterface(1, 261, 110);
				hasHouseOptionsOpen = false;
			} else {
				setInterface(1, 56, 77); //? 56 Seems to make screen Clear
			}
			break;

		case ClientPackets.MOUSE_MOVED:
		case ClientPackets.MOUSE_PRESSED:// mouse pressed
			inStream.readOffset += packetSize;
			break;
		case ClientPackets.MINIMAP_WALK:
			// has 14 bytes of junk at the end
			inStream.writeOffset -= 14;
			packetSize -= 14;
		case ClientPackets.OBJECT_WALK:
		case ClientPackets.REGULAR_WALK:
			resetWalkAction();
			resetWalkingQueue();

			int numPath = (packetSize - 5) / 2;
			int[] pathX = new int[numPath];
			int[] pathY = new int[numPath];

			for (int i = 0; i < numPath; i++) {
				pathX[i] = inStream.readSignedByte();
				pathY[i] = inStream.readSignedByteS();
			}

			int firstX = inStream.readUnsignedWordA() - (mapRegion[0] - 6) * 8;
			int firstY = inStream.readUnsignedWordBigEndian()
					- (mapRegion[1] - 6) * 8;
			inStream.readUnsignedByte();
			addToWalkingQueue(firstX, firstY);

			for (int i = 0; i < numPath; i++) {
				pathX[i] += firstX;
				pathY[i] += firstY;
				addToWalkingQueue(pathX[i], pathY[i]);
			}
			break;

		case ClientPackets.CHAT_TEXT:
			chatEffects = inStream.readUnsignedWord();
			int numChars = inStream.readUnsignedByte();

			chatText = misc.decryptPlayerChat2(inStream, numChars);
			System.out.println("[" + playerName + "] chat: " + chatText);

			updateRequired = true;
			chatTextUpdateRequired = true;
			break;

		case ClientPackets.PLAYER_COMMAND:
			String playerCommand = inStream.readString();
			handlePlayerCommand(playerCommand);
			break;
			
		case ClientPackets.CHARACTER_DESIGN:
			int[] option = new int[6];
			for(int i = 0; i < option.length; i++){
				option[i] = inStream.readUnsignedByte();
				playerColor[i] = option[i];
			}
			playerDesigned = 1;
			setInterface(1, 56, 77);
			updateRequired = true;
		    appearanceUpdateRequired = true;
			break;

		case ClientPackets.PING:// ping

			break;
		case ClientPackets.PING_REPLY:
			inStream.readOffset += packetSize;
			break;
		// For Later Use
		case ClientPackets.EXAMINE_NPC:
		case ClientPackets.CLICK_NPC:
		case ClientPackets.EXAMINE_OBJECT:
		case ClientPackets.CLICK_OBJECT:
		case ClientPackets.MAGIC_ON_OBJECT:
		case ClientPackets.MAGIC_ON_NPC:
		case ClientPackets.MAGIC_ON_ITEM:
			break;
			
		// Unidentifyed Packets (Maybe Anti-Cheat)
		case ClientPackets.UNIDENTIFIED_255:
			break;
		
		default: // Missed/Unfound Packets
			misc.println("Unidentified Packet " + packetType + " size of " + packetSize);
			printBuffer(packetSize);
			return false;
		}
		return true;
	}

	public void process() {
		try {
			processWalking();

		} catch (Exception ex) {
			ex.printStackTrace();
		}
		// postProcessing();

		// directFlushOutStream();

	}

	public void postProcessing() {
		try {
			updateThisPlayer(outStream);

			updateNPCs(outStream);

			if (inventoryUpdateRequired)
				updateInventory();
			/*
			 * updateThisPlayerMovement(outStream);
			 * 
			 * outStream.writeBits(8, 0); // outStream.writeBits(11, 2047);
			 * outStream.finishBitAccess(); outStream.endFrameVarSizeWord();
			 */
			directFlushOutStream();
		} 
		catch (SocketException sex) {}
		catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	public void disconnect() {
		try {
			out.write(40);
			out.flush();
			mySock.close();
		} catch (IOException io) {
		}
	}

	private void directFlushOutStream() throws IOException {
		out.write(outStream.buffer, 0, outStream.writeOffset);
		outStream.writeOffset = 0; // reset
	}

	private void fillInStream(int forceRead) throws IOException {
		int rd = in.read(inStream.buffer, inStream.writeOffset, forceRead);
		inStream.writeOffset += forceRead;
	}

	private void printBuffer(int len) {
		for (int i = 0; i < len; i++) {
			System.out.print(inStream.readUnsignedByte() + " ");
		}
		misc.println(".");
	}

	private void addPingPacket() {
		outStream.createFrame(ServerPackets.PING);
		outStream.writeSize(6);
		outStream.writeByte(1);
		outStream.writeDWord(pingNumber++);
		outStream.writeByte(255);
	}

	public void sendMessageFrame(String str) {
		outStream.createFrame(ServerPackets.SEND_INGAME_MESSAGE);
		outStream.writeByte(str.length() + 1);
		outStream.writeString(str);
	}
	
	public void sendStringFrame(String str, int i, int j, int k, int l) {
		int sSize = str.length() + 5;
		outStream.createFrame(ServerPackets.SET_STRING);
		outStream.writeByte(sSize / 256);
		outStream.writeByte(sSize % 256);
		outStream.writeByte(i);
		outStream.writeByte(j);
		outStream.writeByte(k);
		outStream.writeByte(l);
		outStream.writeString(str);
	}

	public void sendSubMenuFrame(String str, int i, int j) {
		int pSize = str.length() + 3;
		outStream.createFrame(ServerPackets.SET_SUBMENU);
		outStream.writeByte(pSize);
		outStream.writeByte(i);
		outStream.writeString(str);
		outStream.writeByteC(j);
	}

	public void setConfig(int itemID, int value) {
		outStream.createFrame(ServerPackets.SET_CONFIG);
		outStream.writeWord(itemID);
		outStream.writeByteA(value);
	}

	public void setSkillLevelXP(int skillID, int skillLevel, int skillXP) {
		outStream.createFrame(ServerPackets.SET_SKILL_LEVEL_XP);
		outStream.writeByte(skillID);
		outStream.writeDWord_v2(skillXP);
		outStream.writeByte(skillLevel);
	}

	public void setInterface(int i, int interfaceID, int interfaceFrame) {
		outStream.createFrame(ServerPackets.SET_INTERFACE);// cleans buggy
															// interface
		outStream.writeByteS(i);
		outStream.writeWordBigEndian(interfaceID);
		outStream.writeWord(interfaceFrame);
		outStream.writeWord(548);
	}
	
	
	private void highlightSprite(int i, int j) {
		// This Frame Is To Force Buttons That Are Switchable To Change Such As Emotes, To make them colored and not black.
		outStream.createFrame(ServerPackets.LOGIN_FRAME10);
		outStream.writeDWordBigEndian(i);
		outStream.writeWord(j);
	}

	private void setRunEnergy(int energy) {
		outStream.createFrame(ServerPackets.SET_RUN_ENERGY);
		outStream.writeByte(energy);
	}

	/*
	 * SECTION: frames
	 */

	private void frame5(int i) {
		outStream.createFrame(ServerPackets.LOGIN_FRAME5);
		outStream.writeByte(i);
	}

	private void frame66(int i) {
		outStream.createFrame(ServerPackets.LOGIN_FRAME66);
		outStream.writeWord(i);
	}

	private void frame100() {
		outStream.createFrame(ServerPackets.LOGIN_FRAME100);
	}

	private void frame138() {
		outStream.createFrame(ServerPackets.LOGIN_FRAME138);
	}

	private void frame156() {
		outStream.createFrame(ServerPackets.LOGIN_FRAME156);
	}

	private void frame169(int i, int j) {
		outStream.createFrame(ServerPackets.LOGIN_FRAME169);
		outStream.writeByteS(i);
		outStream.writeByteA(j);
	}

	/*
	 * SECTION: UPDATE has all the methods with player updating
	 */

	public stream createPlayerAppearanceStream() {
		stream str = new stream(new byte[57]);
		str.writeByte(playerColor[0]); // gender
		str.writeByte(playerSkull); // skull
		str.writeByte(playerIcons); // headicon
		if (playerEquipment[playerHat] > 0) {
			str.writeWord(0x200 + playerEquipment[playerHat]); // hat
		} else {
			str.writeByte(0);
		}
		if (playerEquipment[playerCape] > 0) {
			str.writeWord(0x200 + playerEquipment[playerCape]); // cape
		} else {
			str.writeByte(0);
		}
		if (playerEquipment[playerAmulet] > 0) {
			str.writeWord(0x200 + playerEquipment[playerAmulet]); // amulet
		} else {
			str.writeByte(0);
		}
		if (playerEquipment[playerWeapon] > 0) {
			str.writeWord(0x200 + playerEquipment[playerWeapon]); // weapon
		} else {
			str.writeByte(0);
		}
		if (playerEquipment[playerChest] > 0) {
			str.writeWord(0x200 + playerEquipment[playerChest]); // torso
		} else {
			str.writeWord(0x100 + pTorso);
		}
		if (playerEquipment[playerShield] > 0) {
			str.writeWord(0x200 + playerEquipment[playerShield]); // shield
		} else {
			str.writeByte(0);
		}
		if (playerEquipment[playerChest] < 1) {
			str.writeWord(0x100 + pArms); // arms
		} else {
			str.writeByte(0);
		}
		if (playerEquipment[playerLegs] > 0) {
			str.writeWord(0x200 + playerEquipment[playerLegs]); // legs
		} else {
			str.writeWord(0x100 + pLegs);
		}
		if (playerEquipment[playerHat] < 1) {// head
			str.writeWord(0x100 + pHead);
		} else {
			str.writeByte(0);
		}
		if (playerEquipment[playerHands] > 0) {
			str.writeWord(0x200 + playerEquipment[playerHands]); // hands
		} else {
			str.writeWord(0x100 + pHands);
		}
		if (playerEquipment[playerFeet] > 0) {
			str.writeWord(0x200 + playerEquipment[playerFeet]); // boots (feet)
		} else {
			str.writeWord(0x100 + pFeet);
		}
		if (playerColor[0] == 0) {
			str.writeWord(0x100 + pBeard); // beard
		} else {
			str.writeByte(0);
		}
		for (int i = 1; i <= 5; i++) {
			str.writeByte(playerColor[i]);
		}
		str.writeWord(getStandAnim()); // stand anim
		str.writeWord(getStandTurnAnim()); // stand turn anim
		str.writeWord(getWalkAnim()); // walk anim
		str.writeWord(getTurn180Anim()); // turn 180 anim
		str.writeWord(getTurnCAnim()); // turn 90 anim C
		str.writeWord(getTurnCCAnim()); // turn 90 anim CC
		str.writeWord(getRunAnim()); // run anim
		str.writeQWord(getName());
		str.writeByte(getCombat()); // Combat Level
		str.writeWord(0);
		return str;
	}

	public int getCombat(){
		int playerCombat = 0;
		int magic = (int)((double)(getLevelForXP(playerXP[4])) * 1.5);
		int range = (int)((double)(getLevelForXP(playerXP[6])) * 1.5);
		int attack = (int)((double)(getLevelForXP(playerXP[0])));
		int strength = (int)((double)(getLevelForXP(playerXP[2])));
		int melee = attack+strength;
		if (range > melee) {
			playerCombat = 
				(int)(((double)(getLevelForXP(playerXP[1])) * 0.25) 
				+ ((double)(getLevelForXP(playerXP[3])) * 0.25) 
				+ ((double)(getLevelForXP(playerXP[5])) * 0.125) 
				+ ((double)(getLevelForXP(playerXP[6])) * 0.4875));
		} else if (magic > melee) {
			playerCombat = (int)(((double)(getLevelForXP(playerXP[1])) * 0.25) + ((double)(getLevelForXP(playerXP[3])) * 0.25) + ((double)(getLevelForXP(playerXP[5])) * 0.125) + ((double)(getLevelForXP(playerXP[4])) * 0.4875));
		} else {
			playerCombat = (int)(((double)(getLevelForXP(playerXP[1])) * 0.25) + ((double)(getLevelForXP(playerXP[3])) * 0.25) + ((double)(getLevelForXP(playerXP[5])) * 0.125) + ((double)(getLevelForXP(playerXP[0])) * 0.325) + ((double)(getLevelForXP(playerXP[2])) * 0.325));
		}
		return playerCombat;
	}
	
	public int getLevelForXP(int exp) {
		int points = 0;
		int output = 0;

		for (int lvl = 1; lvl <= 150; lvl++) {
			points += Math.floor((double)lvl + 300.0 * Math.pow(2.0, (double)lvl / 7.0));
			output = (int)Math.floor(points / 4);
			if (output >= exp)
				return lvl;
		}
		return 0;
	}
	
	public boolean addSkillXP(double amount, int skill){
		int[] playerSkill = new int[25];
		for(int i = 0; i < playerSkill.length; i++){
			playerSkill[i] = getLevelForXP(playerXP[i]);
		}
		if((amount + playerXP[skill]) < 0 
		|| playerXP[skill] > 2000000000) {
			return false;
		}
		int oldLevel = getLevelForXP(playerXP[skill]);
		playerXP[skill] += amount;
		if (oldLevel < getLevelForXP(playerXP[skill])) {
			for(int i = 0; i < playerSkill.length; i++) {
				if (playerSkill[i] < getLevelForXP(playerXP[i])) {
		            playerLevel[i] = getLevelForXP(playerXP[i]);
		            levelupMessage(i);
		            updateRequired = true;
		            appearanceUpdateRequired = true;
				}
			}
			playerLevel[skill] = getLevelForXP(playerXP[skill]);
			updateRequired = true;
			appearanceUpdateRequired = true;
		}
		setSkillLevelXP(skill, playerLevel[skill], playerXP[skill]);
		return true;
	}
	
	//Need Re-aranging...
	public String[] skillName = {"Agility", "Attack", "Cooking", "Crafting", "Defence", "Farming", "Firemaking", "Fishing", "Fletching", "Herblore", "Hitpoints", "Magic", "Mining", "Prayer", "Range", "Runecrafting", "Slayer", "Smithing", "Strength", "Thieving", "Woodcutting", "Construction", "Hunter"};
	public int[] levelupInterface = {157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 395, -1};
	
	public void levelupMessage(int skillID) {
			//doGraphics(0, 0, 0); //need levelup fireworks
			setInterface(1, levelupInterface[skillID], 90);
			sendMessageFrame("Congratulations you have just advanced a "+skillName[skillID]+" level!");
			sendStringFrame("Congratulations you have just advanced a "+skillName[skillID]+" level!", levelupInterface[skillID], 0, 0, 0);
			sendStringFrame("your "+skillName[skillID]+" level is now "+getLevelForXP(playerXP[skillID])+".", levelupInterface[skillID], 0, 1, 0);
			sendStringFrame("Click here to continue", levelupInterface[skillID], 0, 2, 0);
	}
	
	public long getName() {
		return misc.playerNameToLong(playerName);
	}
	
	private void updateThisPlayer(stream str) {
		stream updateStream = new stream(new byte[9999]);

		updateThisPlayerMovement(str);

		str.writeBits(8, playerList.size());

		// for (RSPlayer plr : playerList) {
		int i = 0;
		while (i < playerList.size()) {
			RSPlayer plr = playerList.get(i);
			if (plr == null) {
				System.out.println("Null player list item");
			}
			if (plr == null || plr.destroy || !isInAreaDistance(plr)) {
				str.writeBits(1, 1);
				str.writeBits(2, 3);
				playerList.remove(i);
			} else {
				plr.updatePlayerMovement(str);
				plr.appendPlayerUpdate(updateStream);
				i++;
				// str.writeBits(1, 0);
			}
		}
		// }
		/*
		 * for (int j = 0; j < plist.size(); j++) { if (plist.get(j) > 0)
		 * outStream.writeBits(1, 0); else { outStream.writeBits(1, 1);
		 * outStream.writeBits(2, 3); } }
		 */
		/*
		 * for (int j = 0; j < plist.size(); j++) { if (plist.get(j) <= 0)
		 * plist.remove(j); }
		 */

		for (RSPlayer rsp : server.playerHandler.players) {
			if (rsp != null && rsp.loaded && isInAreaDistance(rsp)
					&& !rsp.destroy && rsp != this && !playerList.contains(rsp)) {
				playerList.add(rsp);

				str.writeBits(11, rsp.playerID);
				str.writeBits(1, 1);
				str.writeBits(3, 0);

				int z = rsp.location[0] - location[0];
				if (z < 0)
					z += 32;
				str.writeBits(5, z);
				str.writeBits(1, 1);
				z = rsp.location[1] - location[1];
				if (z < 0)
					z += 32;
				str.writeBits(5, z);
				boolean flag1 = rsp.updateRequired;
				boolean flag2 = rsp.appearanceUpdateRequired;
				rsp.appearanceUpdateRequired = true;
				rsp.updateRequired = true;
				rsp.appendPlayerUpdate(updateStream);
				rsp.updateRequired = flag1;
				rsp.appearanceUpdateRequired = flag2;
			}
		}
		str.writeBits(11, 2047);
		str.finishBitAccess();
		appendPlayerUpdate(str);
		str.writeBytes(updateStream.buffer, updateStream.writeOffset, 0);
		str.endFrameVarSizeWord();
	}

	public int getStandAnim() {
		switch (playerEquipment[playerWeapon]) {
			case 11694://Godswords
			case 11700:
			case 11698:
			case 11696:
			case 11730:
				return 7047;
			case 4755:
				return 1832;
			default: //Normal
				return 0x328;
		}
	}
	public int getRunAnim() {
		switch (playerEquipment[playerWeapon]) {
			case 11694://Godswords
			case 11700:
			case 11698:
			case 11696:
			case 11730:
				return 7039;
			case 4151://Whip
				return 1661;
			case 4755://Flail
				return 1831;
			default://Normal
				return 0x338;
		}
	}
	public int getWalkAnim() {
		switch (playerEquipment[playerWeapon]) {

			case 11694://Godswords
			case 11700:
			case 11698:
			case 11696:
			case 11730:
				return 7046;
			case 4151://Whip
				return 1660;
			case 4755://Flail
				return 1830;
			default://normal
				return 0x333;
		}
	}
	public int getStandTurnAnim() {
		switch (playerEquipment[playerWeapon]) {
			case 11694://Godswords
			case 11700:
			case 11698:
			case 11696:
			case 11730:
				return 7040;
			default:
				return 0x337;
		}
	}
	public int getTurn180Anim() {
		switch (playerEquipment[playerWeapon]) {
			case 11694://Godswords
			case 11700:
			case 11698:
			case 11696:
			case 11730:
				return 7045;
			default:
				return 0x334;
		}
	}
	public int getTurnCAnim() {
		switch (playerEquipment[playerWeapon]) {
			case 11694://Godswords
			case 11700:
			case 11698:
			case 11696:
			case 11730:
				return 7044;
			default:
				return 0x335;
		}
	}
	public int getTurnCCAnim() {
		switch (playerEquipment[playerWeapon]) {
			case 11694://Godswords
			case 11700:
			case 11698:
			case 11696:
			case 11730:
				return 7043;
			default:
				return 0x336;
		}
	}
	
	private String doMask40 = "";
	private boolean mask40UpdateRequired = false;
	
	public void doMask40(String s) {
		updateRequired=true;
		mask40UpdateRequired=true;
		doMask40=s;
	}
	
	private void appendMask40Update(stream str) {
		str.writeString(doMask40);
	}
	
	private int doMask2 = -1;
	private boolean mask2UpdateRequired = false;
	
	public void doMask2(int i) {
		updateRequired=true;
		mask2UpdateRequired=true;
		doMask2=i;
	}
	
	private void appendMask2Update(stream str) {
		str.writeWord(doMask2);
	}
	
	private boolean mask4UpdateRequired = false;
	
	public void doMask4() {
		updateRequired=true;
		mask4UpdateRequired=true;
	}
	
	private void appendMask4Update(stream str) {
		str.writeByte(255);
		str.writeByteA(4);
		str.writeByteS(0);
		str.writeByteC(255);
	}
	
	private boolean mask400UpdateRequired = false;
	
	public void doMask400() {//unknown
		updateRequired=true;
		mask400UpdateRequired=true;
	}
	
	private void appendMask400Update(stream str) {
		str.writeByteC(currentLocation[0]+10);
		str.writeByteS(currentLocation[1]);
		str.writeByteS(currentLocation[0]);
		str.writeByteC(currentLocation[1]);
		str.writeWord(128);
		str.writeWordA(256);
		str.writeByte(128);
	}
	
	private void appendPlayerUpdate(stream str) {
		if (!updateRequired)
			return;
		int updateMask = 0;
		if (appearanceUpdateRequired)
			updateMask |= 0x10;
		if (graphicsUpdateRequired)
			updateMask |= 0x100;
		if (faceToUpdateRequired)
			updateMask |= 0x1;
		if (mask40UpdateRequired)
			updateMask |= 0x40;
		if (animationUpdateRequired)
			updateMask |= 0x8;
		if (mask2UpdateRequired)
			updateMask |= 0x2;
		if (mask4UpdateRequired)
			updateMask |= 0x4;
		if (chatTextUpdateRequired)
			updateMask |= 0x80;
		if (hitUpdateRequired)
			updateMask |= 0x200;
		if (mask400UpdateRequired)
			updateMask |= 0x400;

		if (updateMask >= 0x100) {
			updateMask |= 0x20;
			str.writeByte(updateMask & 0xFF);
			str.writeByte(updateMask >> 8);
		} else
			str.writeByte(updateMask);
		if (appearanceUpdateRequired)
			appendAppearanceUpdate(str);
		if (graphicsUpdateRequired)
			appendGraphicsUpdate(str);
		if (faceToUpdateRequired)
			appendFaceToUpdate(str);
		if (mask40UpdateRequired)
			appendMask40Update(str);
		if (animationUpdateRequired)
			appendAnimationUpdate(str);
		if (mask2UpdateRequired)
			appendMask2Update(str);
		if (mask4UpdateRequired)
			appendMask4Update(str);
		if (chatTextUpdateRequired)
			appendChatTextUpdate(str);
		if (hitUpdateRequired)
			appendHitUpdate(str);
		if (mask400UpdateRequired)
			appendMask400Update(str);

	}

	private void appendAnimationUpdate(stream str) {
		str.writeWordA((animationUpdateID == -1) ? 65535 : animationUpdateID);
		str.writeByte(animationUpdateDelay);
	}

	private void appendAppearanceUpdate(stream str) {
		stream playerAppearance = createPlayerAppearanceStream();
		str.writeByteA(playerAppearance.available());
		str.writeBytes_reverse(playerAppearance.buffer, playerAppearance
				.available(), 0);
	}

	private void appendChatTextUpdate(stream updateStream) {
		updateStream.writeWordBigEndianA(chatEffects);// effects
		updateStream.writeByteA(playerRights);// rights

		stream str = new stream();
		str.writeByte(chatText.length());
		str.writeOffset += misc.encryptPlayerChat(str.buffer, 0,
				str.writeOffset, chatText.length(), chatText.getBytes());

		byte[] txt = new byte[str.writeOffset];
		str.readBytes(txt, str.writeOffset, 0);

		updateStream.writeByteS(txt.length);
		updateStream.writeBytes(txt, txt.length, 0);
	}

	private void appendFaceToUpdate(stream updateStream) {
		updateStream.writeWordBigEndian(0);
		updateStream.writeWordBigEndianA(0);
	}

	private void appendHitUpdate(stream updateStream) {
		updateStream.writeByteS(hitUpdateDamage);
		updateStream.writeByteS(hitUpdateType);
		updateStream.writeByte(playerLevel[playerHitpoints]);
		updateStream.writeByte(playerLevel[playerHitpoints]);
	}
	
	private void appendGraphicsUpdate(stream str) {
		str.writeWordA(graphicsUpdateID);
		str.writeDWord_v2(graphicsUpdateDelay);
	}

	private void updateThisPlayerMovement(stream str) {

		if (mapRegionDidChange) {
			str.createFrameVarSizeWord(ServerPackets.SET_MAP_REGION);
			str.writeWordBigEndianA(currentLocation[1]);
			str.writeByte(location[2]);
			str.writeWord(mapRegion[0]);

			boolean bool_33_ = false;

			if ((((mapRegion[0] / 8) == 48) || ((mapRegion[0] / 8) == 49))
					&& ((mapRegion[1] / 8) == 48)) {
				bool_33_ = true;
			}

			if (((mapRegion[0] / 8) == 48) && ((mapRegion[1] / 8) == 148)) {
				bool_33_ = true;
			}

			int i_28_ = 0;

			for (int i_34_ = (mapRegion[0] - 6) / 8; i_34_ <= ((mapRegion[0] + 6) / 8); i_34_++) {
				for (int i_35_ = (mapRegion[1] - 6) / 8; i_35_ <= ((mapRegion[1] + 6) / 8); i_35_++) {
					int i_36_ = i_35_ + (i_34_ * 256);

					if (!bool_33_
							|| ((i_35_ != 49) && (i_35_ != 149)
									&& (i_35_ != 147) && (i_34_ != 50) && ((i_34_ != 49) || (i_35_ != 47)))) {

						int[] mapData = WorldData.getMapAreaData(i_36_);
						str.writeDWordBigEndian(mapData[0]);
						str.writeDWordBigEndian(mapData[1]);
						str.writeDWordBigEndian(mapData[2]);
						str.writeDWordBigEndian(mapData[3]);
						i_28_++;
					}
				}
			}
			str.writeWordA(currentLocation[0]);
			str.writeWord(mapRegion[1]);
			str.endFrameVarSizeWord();
			str.createFrameVarSizeWord(ServerPackets.PLAYER_UPDATE);
			str.initBitAccess();
			str.writeBits(1, 1);
			str.writeBits(2, 3);
			str.writeBits(1, (updateRequired) ? 1 : 0);
			str.writeBits(1, didTeleport ? 1 : 0);
			str.writeBits(7, currentLocation[0]);
			str.writeBits(2, location[2]);
			str.writeBits(7, currentLocation[1]);
			return;
		}

		if (dir1 == -1) {
			str.createFrameVarSizeWord(ServerPackets.PLAYER_UPDATE);
			str.initBitAccess();

			if (updateRequired) {
				str.writeBits(1, 1);
				str.writeBits(2, 0);
			} else {
				str.writeBits(1, 0);
			}
		} else {
			str.createFrameVarSizeWord(ServerPackets.PLAYER_UPDATE);
			str.initBitAccess();
			str.writeBits(1, 1);

			if (dir2 == -1) {
				str.writeBits(2, 1);
				str.writeBits(3, dir1);

				if (updateRequired) {
					str.writeBits(1, 1);
				} else {
					str.writeBits(1, 0);
				}
			} else {
				str.writeBits(2, 2);
				str.writeBits(3, dir1);
				str.writeBits(3, dir2);
				if (updateRequired) {
					str.writeBits(1, 1);
				} else {
					str.writeBits(1, 0);
				}
			}
		}
	}

	private void updatePlayerMovement(stream str) {

		if (didTeleport) {
		}

		if (dir1 == -1) {
			if (updateRequired) {
				str.writeBits(1, 1);
				str.writeBits(2, 0);
			} else {
				str.writeBits(1, 0);
			}
		} else {
			str.writeBits(1, 1);

			if (dir2 == -1) {
				str.writeBits(2, 1);
				str.writeBits(3, dir1);
				if (updateRequired) {
					str.writeBits(1, 1);
				} else {
					str.writeBits(1, 0);
				}
			} else {
				str.writeBits(2, 2);
				str.writeBits(3, dir1);
				str.writeBits(3, dir2);
				if (updateRequired) {
					str.writeBits(1, 1);
				} else {
					str.writeBits(1, 0);
				}
			}
		}
	}

	private void resetPlayerUpdateVars() {
		updateRequired = false;
		animationUpdateRequired = false;
		appearanceUpdateRequired = false;
		chatTextUpdateRequired = false;
		faceToUpdateRequired = false;
		graphicsUpdateRequired = false;
		hitUpdateRequired = false;
		mask2UpdateRequired = false;
		mask4UpdateRequired = false;
		mask40UpdateRequired = false;
		mask400UpdateRequired = false;
	}

	public void move() {
		outStream.createFrameVarSizeWord(ServerPackets.NPC_UPDATE);
		outStream.initBitAccess();

		outStream.writeBits(8, npcList.size());
		for (int i = 0; i < npcList.size(); i++)
			outStream.writeBits(1, 0);

		npcList.add(new RSNPC());
		outStream.writeBits(15, npcList.size());
		outStream.writeBits(5, 0);
		outStream.writeBits(14, 10);
		outStream.writeBits(5, 0);
		outStream.writeBits(1, 0);
		outStream.writeBits(3, 0);
		outStream.writeBits(1, 1);
		outStream.finishBitAccess();
		outStream.endFrameVarSizeWord();
	}

	public void face() {
		outStream.createFrameVarSizeWord(ServerPackets.PLAYER_UPDATE);
		outStream.initBitAccess();
		outStream.writeBits(1, 0);
		outStream.writeBits(8, playerList.size());
		for (int j = 0; j < playerList.size(); j++) {
			outStream.writeBits(1, 0);
		}
		playerList.add(this);
		outStream.writeBits(11, 5);
		outStream.writeBits(1, 1);
		outStream.writeBits(3, 0);
		outStream.writeBits(5, 0);
		outStream.writeBits(1, 1);
		outStream.writeBits(5, 0);

		outStream.writeBits(11, 2047);
		outStream.finishBitAccess();

		outStream.writeByte(0x10);
		appendAppearanceUpdate(outStream);

		outStream.endFrameVarSizeWord();
	}

	public void hit() {
		updateRequired = true;
		hitUpdateRequired = true;
	}

	private void updateNPCs(stream str) {
		stream updateStream = new stream(new byte[9999]);

		str.createFrameVarSizeWord(ServerPackets.NPC_UPDATE);
		str.initBitAccess();
		str.writeBits(8, npcList.size());

		int i = 0;
		while (i < npcList.size()) {
			RSNPC npc = npcList.get(i);
			if (npc == null) {
				System.out.println("Null npc list item");
			}
			if (npc == null || npc.destroy || !isInAreaDistance(npc)) {
				str.writeBits(1, 1);
				str.writeBits(2, 3);
				npcList.remove(i);
			} else {
				npc.updateNPCMovement(str);
				npc.appendNPCUpdate(updateStream);
				i++;
			}
		}

		for (RSNPC rsnpc : server.npcHandler.npcs) {
			if (rsnpc != null && isInAreaDistance(rsnpc) && !rsnpc.destroy
					&& !npcList.contains(rsnpc)) {
				npcList.add(rsnpc);

				str.writeBits(15, rsnpc.slot);
				int z = rsnpc.location[0] - location[0];
				if (z < 0)
					z += 32;
				str.writeBits(5, z);
				str.writeBits(14, rsnpc.npcID);
				z = rsnpc.location[1] - location[1];
				if (z < 0)
					z += 32;
				str.writeBits(5, z);
				str.writeBits(1, rsnpc.updateRequired ? 1 : 0);
				str.writeBits(3, 0);
				str.writeBits(1, 1);

				rsnpc.appendNPCUpdate(updateStream);
			}
		}
		if (updateStream.writeOffset > 0)
			str.writeBits(15, 32767);

		str.finishBitAccess();
		str.writeBytes(updateStream.buffer, updateStream.writeOffset, 0);
		str.endFrameVarSizeWord();
	}
	public boolean addItem(int itemID, int itemAmount) {
		int itemSlot = getFreeInventorySlot();
		if (itemSlot == -1 || itemID < 0)
			return false;
		playerItems[itemSlot] = itemID;
		playerItemsN[itemSlot] = itemAmount;
		inventoryUpdateRequired = true;
		return true;
	}

	public void removeItem(int itemSlot) {
		playerItems[itemSlot] = -1;
		playerItemsN[itemSlot] = 0;
		inventoryUpdateRequired = true;
	}

	private int getFreeInventorySlot() {
		for (int i = 0; i < playerItems.length; i++) {
			if (playerItems[i] < 0)
				return i;
		}
		return -1;
	}

	public int getSkillcapeID(){
		switch(playerEquipment[playerCape]){
		case 9747:
		case 9748:
			return 0;
		case 9750:
		case 9751:
			return 1;
		case 9753:
		case 9754:
			return 2;
		case 9756:
		case 9757:
			return 3;
		case 9759:
		case 9760:
			return 4;
		case 9762:
		case 9763:
			return 5;
		case 9765:
		case 9766:
			return 6;
		case 9768:
		case 9769:
			return 7;
		case 9771:
		case 9772:
			return 8;
		case 9774:
		case 9775:
			return 9;
		case 9777:
		case 9778:
			return 10;
		case 9780:
		case 9781:
			return 11;
		case 9783:
		case 9784:
			return 12;
		case 9786:
		case 9787:
			return 13;
		case 9789:
		case 9790:
			return 14;
		case 9792:
		case 9793:
			return 15;
		case 9795:
		case 9796:
			return 16;
		case 9798:
		case 9799:
			return 17;
		case 9801:
		case 9802:
			return 18;
		case 9804:
		case 9805:
			return 19;
		case 9807:
		case 9808:
			return 20;
		case 9810:
		case 9811:
			return 21;
		case 9948:
		case 9949:
			return 22;
		case 10662:
			return 23;
		}
		return -1;
	}
	
	private int lastInventorySlot() {
		for (int i = playerItems.length - 1; i >= 0; i--) {
			if (playerItems[i] > -1)
				return i;
		}
		return 0;
	}

	private void updateInventory() {
		outStream.createFrameVarSizeWord(ServerPackets.LOGIN_FRAME119);
		outStream.writeByte(0);
		outStream.writeByte(149);
		outStream.writeByte(0);
		outStream.writeByte(0);
		outStream.writeByte(0);
		outStream.writeByte(93);
		int itemAmount = lastInventorySlot() + 1;
		outStream.writeWord(itemAmount);
		for (int i = 0; i < itemAmount; i++) {
			if (playerItemsN[i] > 254) {
				outStream.writeByteC(255);
				outStream.writeDWordBigEndian(playerItemsN[i]);
			} else
				outStream.writeByteC(playerItemsN[i]);
			outStream.writeWordBigEndianA(playerItems[i] + 1);
		}
		outStream.endFrameVarSizeWord();
		inventoryUpdateRequired = false;
	}
	private int next = 0;

	private void handlePlayerCommand(String s) {
		if (s == null || s.isEmpty())
			return;

		s = s.toLowerCase();
		StringTokenizer st = new StringTokenizer(s);
		String command = st.nextToken();
		switch (playerRights) {
		case 2:

			if (command.equalsIgnoreCase("item")) {
				try {
					int itemID = Integer.parseInt(st.nextToken());
					int itemAmount = 1;
					if (st.hasMoreTokens())
						itemAmount = Integer.parseInt(st.nextToken());

					addItem(itemID, itemAmount);
				} catch (RuntimeException rex) {
				}
			} else if (command.equalsIgnoreCase("emote")) {
				try {
					int emoteIndex = Integer.parseInt(st.nextToken());
					doAnimation(emoteIndex, 0);
				} catch (RuntimeException rex) {
					
				}
			} else if (command.equalsIgnoreCase("next")) {
				try {
					if (st.hasMoreTokens())
						next = Integer.parseInt(st.nextToken());
					for (int i = 0; i < playerItems.length; i++) {
						playerItems[i] = next++;
						playerItemsN[i] = 1;
					}
					inventoryUpdateRequired = true;
				} catch (RuntimeException rex) {
				}
			} else if (command.equalsIgnoreCase("move")) {
				try {
					int x = Integer.parseInt(st.nextToken());
					int y = Integer.parseInt(st.nextToken());
					resetWalkAction();
					resetWalkingQueue();
					addToWalkingQueue(currentLocation[0] + x, currentLocation[1] + y);

				} catch (RuntimeException rex) {
				}
			} else if (command.equalsIgnoreCase("tele")) {
				try {
					teleportToX = Integer.parseInt(st.nextToken());
					teleportToY = Integer.parseInt(st.nextToken());
				} catch (RuntimeException rex) {
				}
			} else if (command.equalsIgnoreCase("dungeon")) {
				/*
				try {
					teleportToX = location[0];
					teleportToY = location[1] + 6400;
				} catch (RuntimeException rex) {
				}
				*/
			} else if (command.equalsIgnoreCase("surface")) {
				try {
					teleportToX = location[0];
					teleportToY = location[1] - 6400;
				} catch (RuntimeException rex) {
				}
			} else if (command.equalsIgnoreCase("up")) {
				try {
					teleportToX = location[0];
					teleportToY = location[1];
					location[2]++;
				} catch (RuntimeException rex) {
				}
			} else if (command.equalsIgnoreCase("down")) {
				try {
					teleportToX = location[0];
					teleportToY = location[1];
					location[2]--;
				} catch (RuntimeException rex) {
				}
			} else if (command.equalsIgnoreCase("npc")) {
				try {
					int npcID = Integer.parseInt(st.nextToken());
					server.npcHandler.addNewNPC(npcID, location[0], location[1], location[2],
							false);
				} catch (RuntimeException rex) {
				}
			} else if (command.equalsIgnoreCase("del")) {
				try {
					int npcSlot = Integer.parseInt(st.nextToken());
					if (server.npcHandler.npcs[npcSlot] != null) {
						server.npcHandler.npcs[npcSlot].destroy();
					}
				} catch (RuntimeException rex) {
				}
			} else if (command.startsWith("interface")) {
				try {
					int interfaceId = Integer.parseInt(st.nextToken());
					setInterface(1, interfaceId, 77);
				} catch (RuntimeException rex) {
				}
			} else if (command.equalsIgnoreCase("gfx")) {
				try {
					int graphicIndex = Integer.parseInt(st.nextToken());
					doGraphics(graphicIndex, 0, 0);
				} catch (RuntimeException rex) {
				}
			} else if (command.equalsIgnoreCase("addxp")) {
				try {
					int skillID = Integer.parseInt(st.nextToken());
					int skillXP = Integer.parseInt(st.nextToken());
					addSkillXP(skillXP, skillID);
				} catch (RuntimeException rex) {
				}
			} else if (command.equalsIgnoreCase("talk")) {
				try {
					int i1 = Integer.parseInt(st.nextToken());
					String str = st.nextToken("");
					server.npcHandler.npcs[i1].updateRequired = true;
					server.npcHandler.npcs[i1].chatTextUpdateRequired = true;
					server.npcHandler.npcs[i1].chatText = str;
				} catch (RuntimeException rex) {
				}
			} else if (command.equalsIgnoreCase("temp")) {
				try {
					int t1 = Integer.parseInt(st.nextToken());
					outStream.createFrameVarSizeWord(237);
					outStream.writeByte(0);
					outStream.writeByte(0);
					outStream.writeByte(0);
					outStream.writeByte(0);
					outStream.writeByte(0);
					outStream.endFrameVarSizeWord();
				} catch (RuntimeException rex) {
				}
			}

		case 1:

		case 0:
			if (command.equalsIgnoreCase("sc")) {
				
				try {
					int scid = Integer.parseInt(st.nextToken());
					int [] anims = {4977, 4959, 4953, 4955, 4949, 4961, 4963, 4975, 4951, 4937, 4969, 4971, -1, 4939, 4941, 4979, 4973, 4947, 4967, 4943, 4981, -1, 4965, 4957, 4945};
					int []  gfxs = { 830,  823,  820,  821,  818,  824,  825,  831,  819,  812,  835,  833, -1,  813,  814,  829,  832,  817,  827,  815,  828, -1,  826,  822,  816};
					doAnimation(anims[scid], 0);
					doGraphics(gfxs[scid], 0, 0);
				} catch (RuntimeException rex) {
				}
				
			}
		}
	}

	public void handleWalkAction() {

		resetWalkAction();
	}

	private boolean equipItem(int itemID, int slot) {
		if (slot > playerItems.length - 1)
			return false;
		if (itemID != playerItems[slot])
			return false;
		if (Items.itemIs1H(itemID)) {
			if (playerEquipment[playerWeapon] < 0) {
				finalEquip(playerWeapon, slot);
				return true;
			} else {
				replaceEquipment(playerWeapon, slot);
				return true;
			}
		} else if (Items.itemIs2H(itemID)) {
			if (playerEquipment[playerWeapon] < 0) {
				finalEquip(playerWeapon, slot);
				return true;
			} else {
				replaceEquipment(playerWeapon, slot);
				return true;
			}
		} else if (Items.itemIsHelmet(itemID)) {
			if (playerEquipment[playerHat] < 0) {
				finalEquip(playerHat, slot);
				return true;
			} else {
				replaceEquipment(playerHat, slot);
				return true;
			}
		} else if (Items.itemIsCape(itemID)) {
			if (playerEquipment[playerCape] < 0) {
				finalEquip(playerCape, slot);
				return true;
			} else {
				replaceEquipment(playerCape, slot);
				return true;
			}
		} else if (Items.itemIsAmulet(itemID)) {
			if (playerEquipment[playerAmulet] < 0) {
				finalEquip(playerAmulet, slot);
				return true;
			} else {
				replaceEquipment(playerAmulet, slot);
				return true;
			}
		} else if (Items.itemIsArrows(itemID)) {
			if (playerEquipment[playerArrows] < 0) {
				finalEquip(playerArrows, slot);
				return true;
			} else {
				replaceEquipment(playerArrows, slot);
				return true;
			}
		} else if (Items.itemIsTorso(itemID)) {
			if (playerEquipment[playerChest] < 0) {
				finalEquip(playerChest, slot);
				return true;
			} else {
				replaceEquipment(playerChest, slot);
				return true;
			}
		} else if (Items.itemIsLegs(itemID)) {
			if (playerEquipment[playerLegs] < 0) {
				finalEquip(playerLegs, slot);
				return true;
			} else {
				replaceEquipment(playerLegs, slot);
				return true;
			}
		} else if (Items.itemIsGloves(itemID)) {
			if (playerEquipment[playerHands] < 0) {
				finalEquip(playerHands, slot);
				return true;
			} else {
				replaceEquipment(playerHands, slot);
				return true;
			}
		} else if (Items.itemIsBoots(itemID)) {
			if (playerEquipment[playerFeet] < 0) {
				finalEquip(playerFeet, slot);
				return true;
			} else {
				replaceEquipment(playerFeet, slot);
				return true;
			}
		} else if (Items.itemIsRing(itemID)) {
			if (playerEquipment[playerRing] < 0) {
				finalEquip(playerRing, slot);
				return true;
			} else {
				replaceEquipment(playerRing, slot);
				return true;
			}
		}
		return false;
	}

	private void replaceEquipment(int equipmentSlot, int inventorySlot) {
		int itemID = playerEquipment[equipmentSlot];
		int itemAmount = playerEquipmentN[equipmentSlot];

		finalEquip(equipmentSlot, inventorySlot);
		addItem(itemID, itemAmount);
	}

	private void finalEquip(int equipmentSlot, int inventorySlot) {
		playerEquipment[equipmentSlot] = playerItems[inventorySlot];
		playerEquipmentN[equipmentSlot] = playerItemsN[inventorySlot];
		updateRequired = true;
		appearanceUpdateRequired = true;

		removeItem(inventorySlot);
	}
}
